﻿using CSharp.Libs.WPF.UserControls.SmartMediaPlayer.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Input;
using System.IO;
using System.ComponentModel;
using Microsoft.Win32;
using System.Collections;

namespace CSharp.Libs.WPF.UserControls.SmartMediaPlayer.ViewModel
{
    public class PlaylistViewModel : ViewModelBase
	{
		#region declarations
		private DataAccessLayer _dal;
		#endregion
		


		#region constructor
		public PlaylistViewModel()
		{
			//init DataAccessLayer
			_dal = new DataAccessLayer();

			//read play list
			_refreshPlaylist();
		}
		#endregion



		#region event handling
		void _playerControl_CurrentMediaChanged(object sender, CurrentMediaPositionEventArgs ea)
		{
			//call DAL function to update database
            _dal.UpdateCurrentMedia(ea.CurrentItem, ea.CurrentPosition);
		}

		void _playerControl_CurrentMediaEnded(object sender, CurrentMediaEventArgs ea)
		{
			//Get next media and play
			_playNextItem(ea.CurrentItem);
		}

		void _pliSelected_PropertyChanged(object sender, PropertyChangedEventArgs e)
		{
			PlaylistItem pli = sender as PlaylistItem;
			if (pli != null &&
				e.PropertyName == "SortOrder")
			{
				_updateListSortOrder(pli);
			}
		}
		#endregion



		#region private functions
		private void _refreshPlaylist()
		{
			_dal.ReadPlaylist();
			OnPropertyChanged("PlayList");
		}

        private void _playSelected()
        {
            if (PlayerControl != null &&
                SelectedItem != null)
            {
				PlayerControl.VM.PlayFile(SelectedItem);
            }
        }

        private void _readLastPlayed()
        {
            //read database
            PlaylistItem pli = _dal.GetLastPlayedItemAndPosition();

            //if there is one, select it in list and then play it
			PlaylistItem pliSeek = null;
			if (pli != null)
            {
                //cycle through playlist
                foreach (PlaylistItem pliListItem in PlayList)
                {
                    if (pliListItem.Id == pli.Id)
                    {
                        pliSeek = pliListItem;
                        pliSeek.LastPosition = pli.LastPosition;
                        break;
                    }
                }

				//select it in list and then play it
                if (pliSeek != null)
                {
                    SelectedItem = pliSeek;
                    _playSelected();
                }
            }
        }

		/// <summary>
		/// plays next item in list
		/// </summary>
		/// <param name="endedPlaylistItem"></param>
		private void _playNextItem(
			PlaylistItem endedPlaylistItem)
		{
			//init found-switch
			bool _fFoundLastItem = false;
			
			//cycle through list
			PlaylistItem pliSeek = null;
			foreach (PlaylistItem pliListItem in PlayList)
			{
				//take item of the next iteration
				if (_fFoundLastItem)
				{
					pliSeek = pliListItem;
					break;
				}

				//mark ended item as found
				if (pliListItem.Id == endedPlaylistItem.Id)
				{
					_fFoundLastItem = true;
				}
			}

			//if a next item is found, select it in list and then play it
			if (pliSeek != null)
			{
				SelectedItem = pliSeek;
				_playSelected();
			}
		}

		/// <summary>
		/// updates the sort order of all PlaylistItems and saves to db
		/// </summary>
		/// <param name="changedItem"></param>
		private void _updateListSortOrder(
			PlaylistItem changedItem)
		{
			//get old and new index
			int intItemOldIndex = PlayList.IndexOf(changedItem);
			int intItemNewIndex = (int)(changedItem.SortOrder - 1);

			//only do something if index changed
			if (intItemOldIndex != intItemNewIndex)
			{
				//validate new index
				if (intItemNewIndex > PlayList.Count - 1) intItemNewIndex = PlayList.Count - 1;
				if (intItemNewIndex < 0) intItemNewIndex = 0;

				//move the item
				PlayList.Move(intItemOldIndex, intItemNewIndex);

				//rebuild SortOrder and save to db
				int idx = 1;
				foreach (PlaylistItem pli in PlayList)
				{
					pli.SortOrder = idx++;
					_dal.UpdateSortOrder(pli);
				}
			}
		}

		private void _addFiles()
		{
			// Create OpenFileDialog 
			OpenFileDialog dlg = new OpenFileDialog();

			// Set filter for file extension and default file extension 
			//dlg.DefaultExt = ".txt";
			//dlg.Filter = "Text documents (.txt)|*.txt";
			dlg.Multiselect = true;

			// Display OpenFileDialog by calling ShowDialog method 
			Nullable<bool> result = dlg.ShowDialog();

			// Get the selected file name and display in a TextBox 
			if (result == true)
			{
				_dal.AddFilenamesToPlaylist(dlg.FileNames);
			}
		}

		private void _removeItems()
		{
			if (SelectedItems.Count > 0)
			{
                _dal.DeleteItemsFromPlaylist(SelectedItems);
                _refreshPlaylist();
			}
		}
		#endregion
		


		#region methods
		public void Shutdown()
		{
			//unsubscribe
			if (_playerControl != null)
			{
				_playerControl.CurrentMediaChanged -= _playerControl_CurrentMediaChanged;
				_playerControl.CurrentMediaEnded -= _playerControl_CurrentMediaEnded;

				_playerControl = null;
			}

			//DESTROY DataAccessLayer
			_dal.Dispose();
		}
		#endregion
		


		#region properties
		/// <summary>
		/// is the System.Windows.Controls.UserControl that has the MediaElement player
		/// </summary>
		/// <remarks>
		/// IMPORTANT!!! the Name attribute of the MediaElement has to be "mePlayer" to make this all work. :)
		/// 14.07.2014 - Ooops. Sorry. This isn't important at all, since the Player is controlled by its viewmodel. :) See PlayerViewModel.PlayFile() vor details.
		/// </remarks>
		private Player _playerControl;
		public Player PlayerControl
		{
			get
			{
				return _playerControl;
			}
			set
			{
				_playerControl = value;
				
				//subscribe to event from player to be able to write currently payed file and position do database
				_playerControl.CurrentMediaChanged += _playerControl_CurrentMediaChanged;
				_playerControl.CurrentMediaEnded += _playerControl_CurrentMediaEnded;
			
				//read last played media from db and play it
				_readLastPlayed();
			}
		}

		public ObservableCollection<PlaylistItem> PlayList
		{
			get { return _dal.PlayListItems; }
			set
			{
				_dal.PlayListItems = value;
				OnPropertyChanged("PlayList");
			}
		}

        private PlaylistItem _pliSelected;
        public PlaylistItem SelectedItem
        {
            get
            {
                return _pliSelected;
            }
            set
            {
				//if there was an item already, unsubscribe first
				if (_pliSelected != null) _pliSelected.PropertyChanged -= _pliSelected_PropertyChanged;

				if (value == null) return;

				//set new item
				_pliSelected = value;

				//raise PropertyChanged
                OnPropertyChanged("SelectedItem");

				//subscribe to its event
				_pliSelected.PropertyChanged += _pliSelected_PropertyChanged;
            }
        }

        public IList SelectedItems { get; set; }

        private RelayCommand _rcPlaySelectedFile;
        public ICommand PlaySelectedFile
        {
            get
            {
                if (_rcPlaySelectedFile == null)
                {
                    _rcPlaySelectedFile = new RelayCommand(
                        param => this._playSelected(),
                        param => this.CanPlaySelectedFile);
                }
                return _rcPlaySelectedFile;
            }
        }
        public Boolean CanPlaySelectedFile
        {
            get
            {
                return (PlayerControl != null) && (SelectedItem != null) && File.Exists(SelectedItem.Filename);
            }
        }

		private RelayCommand _rcAddFiles;
		public ICommand AddFiles
		{
			get
			{
				if (_rcAddFiles == null)
				{
					_rcAddFiles = new RelayCommand(
						param => this._addFiles(),
						param => this.CanAddFiles);
				}
				return _rcAddFiles;
			}
		}
		public Boolean CanAddFiles
		{
			get
			{
				return (PlayerControl != null);
			}
		}


		private RelayCommand _rcRemoveItems;
		public ICommand RemoveItems
		{
			get
			{
				if (_rcRemoveItems == null)
				{
					_rcRemoveItems = new RelayCommand(
						param => this._removeItems(),
						param => this.CanRemoveItems);
				}
				return _rcRemoveItems;
			}
		}
		public Boolean CanRemoveItems
		{
			get
			{
				return (SelectedItems != null);
			}
		}
		#endregion
	}
}
